<template>
  <view class="content">
    <view class="header">
      <text class="title">RGB & Concentration Analysis</text>
    </view>

    <view class="data-summary" v-if="dataPoints.length > 0">
      <text class="summary-text">Analyzing: {{ dataPoints.length }} data sets</text>
    </view>

    <view class="empty-notice" v-if="dataPoints.length < 2">
      <text>Insufficient data. Please input at least 2 data sets</text>
      <button @click="goToDataInput" type="primary">Input Data</button>
    </view>

    <view class="results-section" v-if="results">
      <view class="description-box">
        <text class="description-text"
          >The system performs linear regression for R, G, B channels and selects the one with R² closest to 0.999 as the optimal channel.</text
        >
      </view>

      <view class="channel-results">
        <view
          class="channel-box"
          :class="{
            'best-channel': bestChannel === 'r',
            'selected-channel': selectedChannel === 'r',
          }"
          @click="switchChannel('r')"
        >
          <text class="channel-title">R Channel (Red)</text>
          <text class="equation" v-if="results"
            >y =
            {{
              regressionType === "linear"
                ? results.r.linear.slope.toFixed(7)
                : results.r.poly.coefficients[0].toFixed(7) +
                  "x² + " +
                  results.r.poly.coefficients[1].toFixed(7) +
                  "x + " +
                  results.r.poly.coefficients[2].toFixed(7)
            }}</text
          >
          <text class="r-squared" v-if="results"
            >R² =
            {{
              regressionType === "linear"
                ? results.r.linear.rSquared.toFixed(7)
                : results.r.poly.rSquared.toFixed(7)
            }}</text
          >
          <text class="r-diff" v-if="rSquaredDiff"
            >Diff: {{ rSquaredDiff.r[regressionType].toFixed(7) }}</text
          >
        </view>
        <view
          class="channel-box"
          :class="{
            'best-channel': bestChannel === 'g',
            'selected-channel': selectedChannel === 'g',
          }"
          @click="switchChannel('g')"
        >
          <text class="channel-title">G Channel (Green)</text>
          <text class="equation" v-if="results"
            >y =
            {{
              regressionType === "linear"
                ? results.g.linear.slope.toFixed(7)
                : results.g.poly.coefficients[0].toFixed(7) +
                  "x² + " +
                  results.g.poly.coefficients[1].toFixed(7) +
                  "x + " +
                  results.g.poly.coefficients[2].toFixed(7)
            }}</text
          >
          <text class="r-squared" v-if="results"
            >R² =
            {{
              regressionType === "linear"
                ? results.g.linear.rSquared.toFixed(7)
                : results.g.poly.rSquared.toFixed(7)
            }}</text
          >
          <text class="r-diff" v-if="rSquaredDiff"
            >Diff: {{ rSquaredDiff.g[regressionType].toFixed(7) }}</text
          >
        </view>
        <view
          class="channel-box"
          :class="{
            'best-channel': bestChannel === 'b',
            'selected-channel': selectedChannel === 'b',
          }"
          @click="switchChannel('b')"
        >
          <text class="channel-title">B Channel (Blue)</text>
          <text class="equation" v-if="results"
            >y =
            {{
              regressionType === "linear"
                ? results.b.linear.slope.toFixed(7)
                : results.b.poly.coefficients[0].toFixed(7) +
                  "x² + " +
                  results.b.poly.coefficients[1].toFixed(7) +
                  "x + " +
                  results.b.poly.coefficients[2].toFixed(7)
            }}</text
          >
          <text class="r-squared" v-if="results"
            >R² =
            {{
              regressionType === "linear"
                ? results.b.linear.rSquared.toFixed(7)
                : results.b.poly.rSquared.toFixed(7)
            }}</text
          >
          <text class="r-diff" v-if="rSquaredDiff"
            >Diff: {{ rSquaredDiff.b[regressionType].toFixed(7) }}</text
          >
        </view>
      </view>

      <view class="best-formula">
        <text class="best-title"
          >Current Channel: {{ getChannelName(selectedChannel || bestChannel) }}</text
        >
        <text class="best-equation"
          >Concentration Formula:
          {{ getConcentrationFormula(selectedChannel || bestChannel) }}</text
        >
      </view>

      <!-- 图表部分 -->
      <view class="chart-container">
        <text class="chart-title"
          >{{ (selectedChannel || bestChannel).toUpperCase() }} Channel vs. Concentration</text
        >
        <view class="chart-area">
          <canvas
            canvas-id="scatterChart"
            id="scatterChart"
            class="chart-canvas"
            style="width: 900px; height: 400px"
            width="700"
            height="400"
          ></canvas>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      dataPoints: [],
      results: null,
      bestChannel: null,
      selectedChannel: null,
      calcInput: {
        r: "",
        g: "",
        b: "",
      },
      calculatedConcentration: null,
      rSquaredDiff: null,
      regressionType: null,
      xAxisLabel: "Concentration (μM)", // Default, might be overridden
    };
  },
  onLoad(options) {
    this.loadData();
    if (options.xAxisLabel) {
      this.xAxisLabel = decodeURIComponent(options.xAxisLabel);
    }
  },
  methods: {
    // 加载数据
    loadData() {
      try {
        const completeData = uni.getStorageSync("completeData");
        if (completeData) {
          this.dataPoints = JSON.parse(completeData);
          console.log("Data loaded successfully, number of data points:", this.dataPoints.length);

          // 如果有足够的数据点，自动计算回归
          if (this.dataPoints.length >= 2) {
            setTimeout(() => {
              this.calculateRegression();
            }, 300);
          }
        } else {
          console.log("No data found");
        }
      } catch (e) {
        console.error("Failed to load data", e);
      }
    },

    // 返回数据输入页面
    goToDataInput() {
      uni.navigateBack();
    },

    // 计算线性回归
    calculateRegression() {
      if (this.dataPoints.length < 3) {
        uni.showToast({
          title: "At least 3 data points are required.",
          icon: "none",
        });
        return;
      }

      // 检查是否有未设置浓度的数据点
      const invalidPoints = this.dataPoints.filter(
        (point) =>
          point.concentration === null || point.concentration === undefined
      );
      if (invalidPoints.length > 0) {
        uni.showToast({
          title: `${invalidPoints.length} data points have no concentration set. Please set them first.`,
          icon: "none",
          duration: 2500,
        });
        return;
      }

      try {
        console.log("Calculating regression, number of data points:", this.dataPoints.length);

        // 计算S0-S/S0
        const processedDataPoints = this.calculateS0MinusSByS0();

        if (!processedDataPoints || processedDataPoints.length < 3) {
          uni.showToast({
            title: "Data processing failed. Please check the data.",
            icon: "none",
          });
          return;
        }

        // 线性回归：X是浓度，Y是S0-S/S0
        const rLinearResult = this.linearRegression(
          processedDataPoints.map((p) => p.concentration), // X: 浓度
          processedDataPoints.map((p) => p.rRatio), // Y: R通道的S0-S/S0
          this.dataPoints.map((p) => p.error || 0) // 误差
        );

        const gLinearResult = this.linearRegression(
          processedDataPoints.map((p) => p.concentration), // X: 浓度
          processedDataPoints.map((p) => p.gRatio), // Y: G通道的S0-S/S0
          this.dataPoints.map((p) => p.error || 0) // 误差
        );

        const bLinearResult = this.linearRegression(
          processedDataPoints.map((p) => p.concentration), // X: 浓度
          processedDataPoints.map((p) => p.bRatio), // Y: B通道的S0-S/S0
          this.dataPoints.map((p) => p.error || 0) // 误差
        );

        // 二次多项式回归
        const rPolyResult = this.polynomialRegression(
          processedDataPoints.map((p) => p.concentration),
          processedDataPoints.map((p) => p.rRatio),
          2 // 二次多项式
        );

        const gPolyResult = this.polynomialRegression(
          processedDataPoints.map((p) => p.concentration),
          processedDataPoints.map((p) => p.gRatio),
          2
        );

        const bPolyResult = this.polynomialRegression(
          processedDataPoints.map((p) => p.concentration),
          processedDataPoints.map((p) => p.bRatio),
          2
        );

        console.log("Linear regression results:", {
          R_Channel: rLinearResult,
          G_Channel: gLinearResult,
          B_Channel: bLinearResult,
        });

        console.log("Polynomial regression results:", {
          R_Channel: rPolyResult,
          G_Channel: gPolyResult,
          B_Channel: bPolyResult,
        });

        // 检查线性回归结果是否有效
        if (
          isNaN(rLinearResult.rSquared) ||
          isNaN(gLinearResult.rSquared) ||
          isNaN(bLinearResult.rSquared)
        ) {
          uni.showToast({
            title: "Error calculating R². Please check the data.",
            icon: "none",
          });
          return;
        }

        // 将线性回归和多项式回归结果都保存
        this.results = {
          r: {
            linear: rLinearResult,
            poly: rPolyResult,
          },
          g: {
            linear: gLinearResult,
            poly: gPolyResult,
          },
          b: {
            linear: bLinearResult,
            poly: bPolyResult,
          },
          processedData: processedDataPoints, // 保存处理后的数据用于绘图
        };

        // 比较线性回归和多项式回归的R²值，选择最好的
        const rLinearDiff = Math.abs(rLinearResult.rSquared - 0.999);
        const gLinearDiff = Math.abs(gLinearResult.rSquared - 0.999);
        const bLinearDiff = Math.abs(bLinearResult.rSquared - 0.999);

        const rPolyDiff = Math.abs(rPolyResult.rSquared - 0.999);
        const gPolyDiff = Math.abs(gPolyResult.rSquared - 0.999);
        const bPolyDiff = Math.abs(bPolyResult.rSquared - 0.999);

        // 保存所有通道的线性和多项式R²差值
        this.rSquaredDiff = {
          r: { linear: rLinearDiff, poly: rPolyDiff },
          g: { linear: gLinearDiff, poly: gPolyDiff },
          b: { linear: bLinearDiff, poly: bPolyDiff },
        };

        console.log("Difference of R² from target (0.999):", {
          Linear: { R: rLinearDiff, G: gLinearDiff, B: bLinearDiff },
          Polynomial: { R: rPolyDiff, G: gPolyDiff, B: bPolyDiff },
        });

        // 找出R²最接近0.999的回归模型和通道
        const allDiffs = [
          {
            channel: "r",
            type: "linear",
            diff: rLinearDiff,
            rSquared: rLinearResult.rSquared,
          },
          {
            channel: "g",
            type: "linear",
            diff: gLinearDiff,
            rSquared: gLinearResult.rSquared,
          },
          {
            channel: "b",
            type: "linear",
            diff: bLinearDiff,
            rSquared: bLinearResult.rSquared,
          },
          {
            channel: "r",
            type: "poly",
            diff: rPolyDiff,
            rSquared: rPolyResult.rSquared,
          },
          {
            channel: "g",
            type: "poly",
            diff: gPolyDiff,
            rSquared: gPolyResult.rSquared,
          },
          {
            channel: "b",
            type: "poly",
            diff: bPolyDiff,
            rSquared: bPolyResult.rSquared,
          },
        ];

        // 按R²差值排序
        allDiffs.sort((a, b) => a.diff - b.diff);

        // 首先检查是否有任何模型的R²值超过0.9
        const goodFitModels = allDiffs.filter((model) => model.rSquared > 0.9);

        if (goodFitModels.length > 0) {
          // 选择最好的模型
          const bestModel = goodFitModels[0];
          this.bestChannel = bestModel.channel;
          this.regressionType = bestModel.type;

          console.log(
            "Optimal model selected:",
            `${this.bestChannel} channel, ${
              this.regressionType === "poly" ? "Polynomial" : "Linear"
            } regression`
          );
        } else {
          // 如果所有模型拟合都不好，选择R²最高的
          allDiffs.sort((a, b) => b.rSquared - a.rSquared);
          const bestModel = allDiffs[0];
          this.bestChannel = bestModel.channel;
          this.regressionType = bestModel.type;

          // 提示用户拟合质量不高
          uni.showToast({
            title: "Low fit quality. Please check the data.",
            icon: "none",
            duration: 2000,
          });
        }

        // 设置当前选中通道为最优通道, 确保saveBestFormula使用正确的通道
        this.selectedChannel = this.bestChannel;

        // 保存最佳公式到本地存储 (内部已有console.log打印formulaData对象)
        this.saveBestFormula();

        // 打印用户请求的公式字符串
        const bestFormulaString = this.getConcentrationFormula(this.selectedChannel);
        console.log(`Best formula calculated on page (${this.getChannelName(this.selectedChannel)}, ${this.regressionType === "poly" ? "Polynomial" : "Linear"}):`, bestFormulaString);

        // 显示计算成功提示
        uni.showToast({
          title: `Calculation complete. Selected ${
            this.regressionType === "poly" ? "Polynomial" : "Linear"
          } Regression, Channel: ${this.getChannelName(
            this.selectedChannel // this.selectedChannel 已被正确设置
          )}`,
          icon: "success",
          duration: 2000,
        });

        // 绘制图表
        this.$nextTick(() => {
          this.drawChart();
        });
      } catch (error) {
        console.error("Error calculating regression:", error);
        uni.showToast({
          title: "Calculation error. Please check the data.",
          icon: "none",
        });
      }
    },

    // 计算S0-S/S0
    calculateS0MinusSByS0() {
      try {
        // 按浓度排序，找出浓度最小的点作为S0
        const sortedData = [...this.dataPoints].sort(
          (a, b) => a.concentration - b.concentration
        );
        const s0 = sortedData[0]; // 假设最小浓度对应的是S0点

        console.log("Using S0 point:", s0);

        // 计算所有点的S0-S/S0，严格按照公式 (S0-S)/S0 计算
        return this.dataPoints.map((point) => {
          // 为了调试，保存详细计算过程
          const rS0 = s0.r;
          const gS0 = s0.g;
          const bS0 = s0.b;
          const rS = point.r;
          const gS = point.g;
          const bS = point.b;

          // 防止除以零
          const rRatio = rS0 !== 0 ? (rS0 - rS) / rS0 : 0;
          const gRatio = gS0 !== 0 ? (gS0 - gS) / gS0 : 0;
          const bRatio = bS0 !== 0 ? (bS0 - bS) / bS0 : 0;

          // 输出每个点的详细计算结果以便调试
          console.log(`Concentration ${point.concentration}:`, {
            R_Channel: `(${rS0} - ${rS}) / ${rS0} = ${rRatio.toFixed(7)}`,
            G_Channel: `(${gS0} - ${gS}) / ${gS0} = ${gRatio.toFixed(7)}`,
            B_Channel: `(${bS0} - ${bS}) / ${bS0} = ${bRatio.toFixed(7)}`,
          });

          return {
            ...point,
            rRatio: isNaN(rRatio) ? 0 : parseFloat(rRatio.toFixed(7)),
            gRatio: isNaN(gRatio) ? 0 : parseFloat(gRatio.toFixed(7)),
            bRatio: isNaN(bRatio) ? 0 : parseFloat(bRatio.toFixed(7)),
          };
        });
      } catch (error) {
        console.error("Error in calculateS0MinusSByS0 function:", error);
        return [];
      }
    },

    // 线性回归计算 - 简化版本，使用最小二乘法
    linearRegression(x, y, errors) {
      try {
        // 检查输入数据有效性
        if (!x || !y || x.length < 2 || y.length < 2 || x.length !== y.length) {
          console.error("Invalid input data for linear regression", { x, y });
          return { slope: 0, intercept: 0, rSquared: 0, stdError: 0 };
        }

        // 过滤掉任何NaN或Infinity值
        const validData = [];
        for (let i = 0; i < x.length; i++) {
          if (
            !isNaN(x[i]) &&
            !isNaN(y[i]) &&
            isFinite(x[i]) &&
            isFinite(y[i])
          ) {
            validData.push({ x: x[i], y: y[i], error: errors[i] || 0 });
          }
        }

        if (validData.length < 2) {
          console.error("Insufficient valid data points", validData.length);
          return { slope: 0, intercept: 0, rSquared: 0, stdError: 0 };
        }

        // 输出用于线性回归的数据
        console.log(
          "Input data for linear regression:",
          validData.map((d) => `Concentration: ${d.x}, Ratio: ${d.y.toFixed(7)}`)
        );

        // 简单线性回归计算
        let sumX = 0,
          sumY = 0,
          sumXY = 0,
          sumX2 = 0,
          sumY2 = 0;
        const n = validData.length;

        for (const point of validData) {
          sumX += point.x;
          sumY += point.y;
          sumXY += point.x * point.y;
          sumX2 += point.x * point.x;
          sumY2 += point.y * point.y;
        }

        const meanX = sumX / n;
        const meanY = sumY / n;

        // 斜率(slope)和截距(intercept)
        const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
        const intercept = meanY - slope * meanX;

        // 计算R²
        const SST = sumY2 - (sumY * sumY) / n; // 总平方和
        let SSE = 0; // 残差平方和

        for (const point of validData) {
          const predicted = slope * point.x + intercept;
          SSE += Math.pow(point.y - predicted, 2);
        }

        const rSquared = 1 - SSE / SST;

        // 计算标准误差
        const stdError = Math.sqrt(SSE / (n - 2));

        // 打印线性回归结果
        console.log("Linear regression result:", {
          Slope: slope.toFixed(7),
          Intercept: intercept.toFixed(7),
          "R²": rSquared.toFixed(7),
          StdError: stdError.toFixed(7),
        });

        // 返回参数结果
        return {
          slope: parseFloat(slope.toFixed(7)),
          intercept: parseFloat(intercept.toFixed(7)),
          rSquared: parseFloat(rSquared.toFixed(7)),
          stdError: parseFloat(stdError.toFixed(7)),
        };
      } catch (error) {
        console.error("Error in linear regression calculation:", error);
        return { slope: 0, intercept: 0, rSquared: 0, stdError: 0 };
      }
    },

    getChannelName(channel) {
      if (!channel) return "";
      const channelMap = {
        r: "R (Red)",
        g: "G (Green)",
        b: "B (Blue)",
      };
      return channelMap[channel];
    },

    getConcentrationFormula(channel) {
      if (!channel || !this.results) return "";
      const channelResult = this.results[channel];
      if (!channelResult) return "";
      if (this.regressionType === "linear") {
        const result = channelResult.linear;
        return `(S₀-S)/S₀ = ${result.slope.toFixed(
          7
        )} × Conc. + ${result.intercept.toFixed(7)}`;
      } else {
        const coeffs = channelResult.poly.coefficients;
        if (coeffs.length === 3) {
          return `(S₀-S)/S₀ = ${coeffs[0].toFixed(
            7
          )} × Conc.² + ${coeffs[1].toFixed(7)} × Conc. + ${coeffs[2].toFixed(
            7
          )}`;
        }
        return "Polynomial Regression Formula";
      }
    },

    drawChart() {
      if (!this.selectedChannel || !this.results) return;

      try {
        const systemInfo = uni.getSystemInfoSync();
        const screenWidth = systemInfo.windowWidth;
        const isPC = screenWidth > 600;
        const scale = isPC ? 1 : screenWidth / 375;
        const width = isPC ? 700 : Math.min(350 * scale, screenWidth - 20);
        const height = isPC ? 400 : Math.min(300 * scale, 350);
        const ctx = uni.createCanvasContext("scatterChart", this);

        const result =
          this.regressionType === "linear"
            ? this.results[this.selectedChannel]?.linear
            : this.results[this.selectedChannel]?.poly;

        if (!result) return;

        const processedData = this.results.processedData;
        const xValues = processedData.map((p) => p.concentration);
        const yValues = processedData.map(
          (p) => p[this.selectedChannel + "Ratio"]
        );
        const errors = this.dataPoints.map((p) => p.error || 0); // errors not used for x-axis scaling, but for drawing error bars
        const xMin = 0;

        let dataActualMaxX = xMin;
        if (xValues.length > 0) {
            dataActualMaxX = Math.max(xMin, ...xValues);
        }

        let rangeEndForStepCalc = dataActualMaxX;
        if (xValues.length === 0) { // No data
            rangeEndForStepCalc = xMin + 20; // Default span for step calculation if empty
        } else if (dataActualMaxX <= xMin) { // All data at xMin (e.g., 0)
            rangeEndForStepCalc = xMin + 20; // Default span for step calculation if flat at xMin
        }

        const finalXStep = this.calculateNiceStep(xMin, rangeEndForStepCalc, 8);

        let finalXAxisMax;
        if (dataActualMaxX > xMin && finalXStep > 0) { 
            finalXAxisMax = Math.ceil(dataActualMaxX / finalXStep) * finalXStep;
        } else if (finalXStep > 0) { 
            finalXAxisMax = xMin + finalXStep * 2; 
        } else { // Ultimate fallback if step was also 0 or ineffective
             finalXAxisMax = dataActualMaxX > xMin ? dataActualMaxX + 10 : xMin + 10; 
        }

        const xAxisMax = finalXAxisMax; // Use the newly calculated max for the x-axis

        const yMin = -0.1;
        let yDataMax = Math.max(...yValues.map((y, i) => y + (errors[i] || 0) * 0.1)) + 0.05;
        const yTickStep = 0.1; // Y-axis ticks every 0.1
        const yAxisLimit = Math.ceil(yDataMax / yTickStep) * yTickStep; // Align yMax to 0.1 step
        
        const xRange = [xMin, xAxisMax];
        const yRange = [yMin, yAxisLimit];

        const padding = {
          left: Math.round(65 * scale), // Increased left padding for Y-axis label
          right: Math.round(30 * scale),
          top: Math.round(50 * scale), // Increased top padding for equation
          bottom: Math.round(60 * scale),
        };

        const chartWidth = width - padding.left - padding.right;
        const chartHeight = height - padding.top - padding.bottom;

        ctx.clearRect(0, 0, width, height);
        ctx.setFillStyle('#f0f0f0'); // Light grey background
        ctx.fillRect(0, 0, width, height);
        
        // Draw inner chart area with white background and border (optional, if you want a distinct inner plot area)
        ctx.setFillStyle('#ffffff');
        ctx.fillRect(padding.left, padding.top, chartWidth, chartHeight);
        ctx.setStrokeStyle('#000000');
        ctx.setLineWidth(1);
        ctx.strokeRect(padding.left, padding.top, chartWidth, chartHeight);


        const xToPixel = (x) => 
            padding.left +
            ((x - xRange[0]) / (xRange[1] - xRange[0])) * chartWidth;
        
        const yToPixel = (y) => 
            height -
            padding.bottom -
            ((y - yRange[0]) / (yRange[1] - yRange[0])) * chartHeight;

        ctx.setLineWidth(0.5); // Thinner grid lines
        ctx.setStrokeStyle("#cccccc"); // Lighter grid lines
        const tickLength = 4 * scale; // 定义刻度线长度, 恢复到较小但可见的值
        const minorTickLength = tickLength * 0.6; // Minor ticks are 60% of major ticks

        const xStep = finalXStep; // Use the xStep calculated based on data range

        for (let yValue = yMin; yValue <= yRange[1] + 1e-8; yValue += yTickStep) { // Use yRange[1] and yTickStep
          if (yValue < yMin) continue; 
          const y = yToPixel(yValue);
          // Comment out Y-axis grid line
          /*
          ctx.beginPath();
          ctx.moveTo(padding.left, y);
          ctx.lineTo(padding.left + chartWidth, y);
          ctx.stroke();
          */
          ctx.setTextAlign("right");
          ctx.setFontSize(Math.round(10 * scale));
          ctx.setFillStyle("#000000");
          ctx.fillText(yValue.toFixed(1), padding.left - 8, y + 4); // Use toFixed(1) for Y labels

          // Draw Y-axis tick marks
          ctx.beginPath();
          ctx.setStrokeStyle("#000000");
          ctx.setLineWidth(1);
          ctx.moveTo(padding.left, y);
          ctx.lineTo(padding.left + tickLength, y); // Modified: Draw tick inside
          ctx.stroke();
        }

        let currentX = xMin;
        while(currentX <= xRange[1] + 1e-8) { // Iterate up to xRange[1]
            const x = xToPixel(currentX);
            // Comment out X-axis grid line
            /*
            ctx.beginPath();
            ctx.moveTo(x, height - padding.bottom);
            ctx.lineTo(x, padding.top);
            ctx.stroke();
            */
            
            if (currentX >= xMin) { // Draw label and major tick
                 ctx.setTextAlign("center");
                 ctx.setFontSize(Math.round(10 * scale));
                 ctx.setFillStyle("#000000");
                 ctx.fillText(currentX.toFixed(0), x, height - padding.bottom + 15);

                // Draw X-axis MAJOR tick marks
                // Skip drawing the line for the very last major tick if it aligns with the axis end.
                if (Math.abs(currentX - xRange[1]) < 1e-7) {
                    // This is the last major tick label at the end of the x-axis range.
                    // Do not draw the vertical line, only the label (already drawn).
                } else {
                    ctx.beginPath();
                    ctx.setStrokeStyle("#000000"); 
                    ctx.setLineWidth(1); 
                    ctx.moveTo(x, height - padding.bottom);
                    ctx.lineTo(x, height - padding.bottom - tickLength); 
                    ctx.stroke();
                }
            }

            // Draw X-axis MINOR tick marks
            const minorTickXVal = currentX + xStep / 2;
            if (minorTickXVal < xRange[1] - 1e-8 && xStep > 0) { // Ensure minor tick is within x-axis range, not at the very end, and xStep is valid
                const minorXPx = xToPixel(minorTickXVal);
                ctx.beginPath();
                ctx.setStrokeStyle("#000000");
                ctx.setLineWidth(1);
                ctx.moveTo(minorXPx, height - padding.bottom);
                ctx.lineTo(minorXPx, height - padding.bottom - minorTickLength); // Use minorTickLength
                ctx.stroke();
            }

            currentX += xStep;
            if(xStep === 0) break; 
        }

        ctx.beginPath();
        ctx.setLineWidth(1);
        ctx.setStrokeStyle("#000000");
        ctx.moveTo(padding.left, height - padding.bottom);
        ctx.lineTo(padding.left + chartWidth, height - padding.bottom);
        ctx.moveTo(padding.left, height - padding.bottom);
        ctx.lineTo(padding.left, padding.top);
        ctx.stroke();

        /*
        ctx.beginPath();
        ctx.moveTo(padding.left + chartWidth, height - padding.bottom);
        ctx.lineTo(padding.left + chartWidth - 6, height - padding.bottom - 3);
        ctx.lineTo(padding.left + chartWidth - 6, height - padding.bottom + 3);
        ctx.closePath();
        ctx.setFillStyle('#000000');
        ctx.fill();
        */

        /*
        ctx.beginPath();
        ctx.moveTo(padding.left, padding.top);
        ctx.lineTo(padding.left - 3, padding.top + 6);
        ctx.lineTo(padding.left + 3, padding.top + 6);
        ctx.closePath();
        ctx.fill();
        */

        for (let i = 0; i < xValues.length; i++) {
          const x = xValues[i];
          const y = yValues[i];
          const error = errors[i] || 0;
          
          const px = xToPixel(x);
          const py = yToPixel(y);

          if (error > 0) {
            const errorScaled = error * (yRange[1] - yRange[0]) / chartHeight * 10; // Example scaling, adjust as needed
            const pyUpper = yToPixel(y + error ); // Use original error for calculation
            const pyLower = yToPixel(y - error );
            
            ctx.beginPath();
            ctx.setStrokeStyle("#000000"); 
            ctx.setLineWidth(1);
            ctx.moveTo(px, pyUpper);
            ctx.lineTo(px, pyLower);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(px - 4, pyUpper);
            ctx.lineTo(px + 4, pyUpper);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(px - 4, pyLower);
            ctx.lineTo(px + 4, pyLower);
            ctx.stroke();
          }
          
          ctx.setFillStyle('#007aff'); // Blue for points
          ctx.setStrokeStyle('#000000');
          ctx.setLineWidth(0.5);
          const pointSize = Math.round(5 * scale);
          ctx.fillRect(px - pointSize / 2, py - pointSize / 2, pointSize, pointSize);
          // ctx.strokeRect(px - pointSize / 2, py - pointSize / 2, pointSize, pointSize); // Optional: if border for points is needed
        }

        ctx.beginPath();
        ctx.setStrokeStyle("#ff0000"); // Red regression line
        ctx.setLineWidth(1.5);
        
        const lineXStart = xMin;
        const lineXEnd = dataActualMaxX; // Regression line will end at the max data x-value
        let lineYStart, lineYEnd;

        if (this.regressionType === 'linear') {
            lineYStart = result.slope * lineXStart + result.intercept;
            lineYEnd = result.slope * lineXEnd + result.intercept;
        } else { // poly
            const c = result.coefficients;
            lineYStart = c[0] * lineXStart * lineXStart + c[1] * lineXStart + c[2];
            lineYEnd = c[0] * lineXEnd * lineXEnd + c[1] * lineXEnd + c[2];
        }
        
        lineYStart = Math.max(yRange[0], Math.min(yRange[1], lineYStart));
        lineYEnd = Math.max(yRange[0], Math.min(yRange[1], lineYEnd));

        ctx.moveTo(xToPixel(lineXStart), yToPixel(lineYStart));
        ctx.lineTo(xToPixel(lineXEnd), yToPixel(lineYEnd));
        ctx.stroke();

        ctx.setTextAlign("center");
        ctx.setFontSize(Math.round(12 * scale));
        ctx.setFillStyle("#000000");
        ctx.fillText(this.xAxisLabel, padding.left + chartWidth / 2, height - Math.round(15*scale));

        ctx.save();
        ctx.translate(padding.left - Math.round(45 * scale), padding.top + chartHeight / 2);
        ctx.rotate(-Math.PI / 2);
        ctx.setTextAlign("center");
        let yAxisText = `S₀-S/S₀`;
        if (this.selectedChannel === 'r') yAxisText += ' [Red channel]';
        else if (this.selectedChannel === 'g') yAxisText += ' [Green channel]';
        else if (this.selectedChannel === 'b') yAxisText += ' [Blue channel]';
        ctx.fillText(yAxisText, 0, 0);
        ctx.restore();

        ctx.setTextAlign("left");
        ctx.setFontSize(Math.round(11 * scale)); 
        ctx.setFillStyle("#000000");
        let formulaText = "";
        let r2Text = "";

        if (this.regressionType === "linear") {
          formulaText = `y = ${result.slope.toFixed(4)}x + ${result.intercept.toFixed(4)}`;
          r2Text = `R² = ${result.rSquared.toFixed(4)}`;
        } else { // Assuming poly is degree 2
          const c = result.coefficients;
          if (c && c.length === 3) {
             formulaText = `y = ${c[0].toFixed(4)}x² + ${c[1].toFixed(4)}x + ${c[2].toFixed(4)}`;
             r2Text = `R² = ${result.rSquared.toFixed(4)}`;
          } else {
            formulaText = "Polynomial formula unavailable";
            r2Text = "R² unavailable";
          }
        }
        ctx.fillText(formulaText, padding.left + 10, padding.top + 15);
        ctx.fillText(r2Text, padding.left + 10, padding.top + 30); 

        ctx.draw();
      } catch (error) {
        console.error("Error drawing chart", error);
        uni.showToast({ title: 'Chart drawing failed.', icon: 'none' });
      }
    },

    // 计算适合的刻度间隔
    calculateNiceStep(min, max, targetSteps) {
      if (min === undefined || max === undefined || targetSteps === undefined || max < min) {
            console.warn("Invalid input for calculateNiceStep", {min, max, targetSteps});
            return 10; // Fallback step
      }
      const range = max - min;
       if (range === 0) {
            return Math.pow(10, Math.floor(Math.log10(min > 0 ? min : 1))) / 10 || 1; // Handle zero range with a sensible fraction of min or 1
       }
      const roughStep = range / targetSteps;

      // 找到合适的刻度单位 (1, 2, 5, 10, 20, 50, etc.)
      const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));
      const normalizedStep = roughStep / magnitude;

      let niceStep;
      if (normalizedStep < 1.5) {
        niceStep = 1;
      } else if (normalizedStep < 3) {
        niceStep = 2;
      } else if (normalizedStep < 7) {
        niceStep = 5;
      } else {
        niceStep = 10;
      }
      const finalStep = niceStep * magnitude;
      return finalStep > 0 ? finalStep : 1; // Ensure step is always positive
    },

    // 保存最佳公式到本地存储
    saveBestFormula() {
      if (!this.selectedChannel || !this.results || !this.regressionType)
        return;

      const channelResult = this.results[this.selectedChannel];
      let formulaData = {};

      if (this.regressionType === "linear") {
        const linearResult = channelResult.linear;
        formulaData = {
          channel: this.selectedChannel,
          regressionType: "linear",
          slope: linearResult.slope,
          intercept: linearResult.intercept,
          rSquared: linearResult.rSquared,
          stdError: linearResult.stdError,
        };
      } else {
        // 多项式回归
        const polyResult = channelResult.poly;
        formulaData = {
          channel: this.selectedChannel,
          regressionType: "poly",
          coefficients: polyResult.coefficients,
          rSquared: polyResult.rSquared,
        };
      }

      try {
        uni.setStorageSync("bestFormula", JSON.stringify(formulaData));
        console.log("Best formula saved:", formulaData);
      } catch (e) {
        console.error("Failed to save formula", e);
      }
    },

    // 二次多项式回归
    polynomialRegression(x, y, degree) {
      try {
        // 检查输入数据有效性
        if (
          !x ||
          !y ||
          x.length < degree + 1 ||
          y.length < degree + 1 ||
          x.length !== y.length
        ) {
          console.error("Invalid input data for polynomial regression", { x, y, degree });
          return { coefficients: [], rSquared: 0, type: "poly" };
        }

        // 过滤掉任何NaN或Infinity值
        const validData = [];
        for (let i = 0; i < x.length; i++) {
          if (
            !isNaN(x[i]) &&
            !isNaN(y[i]) &&
            isFinite(x[i]) &&
            isFinite(y[i])
          ) {
            validData.push({ x: x[i], y: y[i] });
          }
        }

        if (validData.length < degree + 1) {
          console.error("Insufficient valid data points for polynomial regression", validData.length);
          return { coefficients: [], rSquared: 0, type: "poly" };
        }

        // 构建系数矩阵
        const n = validData.length;

        // 创建矩阵A和向量B
        const matrixA = [];
        const vectorB = [];

        // 对于二次多项式 y = a*x^2 + b*x + c
        // 我们需要求解系数 a, b, c
        for (let i = 0; i <= degree; i++) {
          const row = [];
          for (let j = 0; j <= degree; j++) {
            // 计算 sum(x^(i+j))
            let sum = 0;
            for (let k = 0; k < n; k++) {
              sum += Math.pow(validData[k].x, i + j);
            }
            row.push(sum);
          }
          matrixA.push(row);

          // 计算 sum(y*x^i)
          let sum = 0;
          for (let k = 0; k < n; k++) {
            sum += validData[k].y * Math.pow(validData[k].x, i);
          }
          vectorB.push(sum);
        }

        // 求解线性方程组 A * X = B，使用高斯消元法
        const coefficients = this.solveLinearSystem(matrixA, vectorB);

        // 计算R²
        let SST = 0; // 总平方和
        let SSE = 0; // 残差平方和
        const meanYValue = validData.reduce((acc, p) => acc + p.y, 0) / n; // y的平均值

        for (const point of validData) {
          // 计算预测值
          let predicted = 0;
          for (let i = 0; i <= degree; i++) {
            predicted += coefficients[i] * Math.pow(point.x, degree - i); 
          }

          SSE += Math.pow(point.y - predicted, 2);
          SST += Math.pow(point.y - meanYValue, 2);
        }
        
        let rSquared = 0;
        if (SST === 0 && SSE === 0) { // Perfect fit, all points on the line, and line is not horizontal if y values vary
            rSquared = 1;
        } else if (SST === 0 && SSE !== 0) { // Should not happen if SSE is derived from same data; implies y values are constant but model is not predicting them
            rSquared = 0; // Or undefined, as R² is not well-defined here
        } else {
            rSquared = 1 - SSE / SST;
        }

        // 输出多项式系数
        console.log(
          "Polynomial regression coefficients:",
          coefficients.map((c) => c.toFixed(7))
        );
        console.log("Polynomial R²:", rSquared.toFixed(7));

        return {
          coefficients: coefficients.map((c) => parseFloat(c.toFixed(7))),
          rSquared: parseFloat(rSquared.toFixed(7)),
          type: "poly",
        };
      } catch (error) {
        console.error("Error in polynomial regression calculation:", error);
        return { coefficients: [], rSquared: 0, type: "poly" };
      }
    },

    // 求解线性方程组 Ax = b
    solveLinearSystem(A, b) {
      const n = A.length;
      const augmented = [];

      // 创建增广矩阵 [A|b]
      for (let i = 0; i < n; i++) {
        augmented.push([...A[i], b[i]]);
      }

      // 高斯消元
      for (let i = 0; i < n; i++) {
        // 找到当前列中最大元素所在行
        let maxRow = i;
        for (let j = i + 1; j < n; j++) {
          if (Math.abs(augmented[j][i]) > Math.abs(augmented[maxRow][i])) {
            maxRow = j;
          }
        }

        // 交换行
        if (maxRow !== i) {
          [augmented[i], augmented[maxRow]] = [augmented[maxRow], augmented[i]];
        }

        // 主元为0，无法求解
        if (Math.abs(augmented[i][i]) < 1e-10) {
          console.error("Linear system has no unique solution (pivot is zero)");
          return Array(n).fill(0);
        }

        // 将主元归一化
        const pivot = augmented[i][i];
        for (let j = i; j <= n; j++) {
          augmented[i][j] /= pivot;
        }

        // 消元
        for (let j = 0; j < n; j++) {
          if (j !== i) {
            const factor = augmented[j][i];
            for (let k = i; k <= n; k++) {
              augmented[j][k] -= factor * augmented[i][k];
            }
          }
        }
      }

      // 提取解
      const solution = [];
      for (let i = 0; i < n; i++) {
        solution.push(augmented[i][n]);
      }

      return solution;
    },

    switchChannel(channel) {
      this.selectedChannel = channel;
      this.$nextTick(() => {
        this.drawChart();
      });
    },
  },
};
</script>

<style>
.content {
  padding: 20rpx;
}

.header {
  text-align: center;
  margin-bottom: 30rpx;
}

.title {
  font-size: 40rpx;
  font-weight: bold;
  color: #333;
}

.data-summary {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 20rpx;
}

.summary-text {
  font-size: 28rpx;
  color: #666;
}

.empty-notice {
  padding: 50rpx 0;
  text-align: center;
  color: #999;
  font-size: 30rpx;
  display: flex;
  flex-direction: column;
  gap: 20rpx;
  align-items: center;
}

.results-section {
  margin-top: 30rpx;
}

.description-box {
  background-color: #f0f8ff;
  border: 1px solid #add8e6;
  border-radius: 8rpx;
  padding: 15rpx;
  margin-bottom: 15rpx;
}

.description-text {
  font-size: 26rpx;
  color: #333;
  line-height: 1.5;
}

.channel-results {
  display: flex;
  justify-content: space-between;
  margin-bottom: 30rpx;
}

.channel-box {
  flex: 1;
  margin: 0 10rpx;
  padding: 20rpx;
  background-color: #f9f9f9;
  border-radius: 10rpx;
  border: 1px solid #eee;
}

.best-channel {
  border: 2px solid #27ae60;
  background-color: #e8f8f0;
}

.selected-channel {
  border: 2px solid #007aff;
  box-shadow: 0 0 8rpx #b3d8ff;
}

.channel-title {
  font-weight: bold;
  margin-bottom: 10rpx;
  display: block;
}

.equation,
.r-squared {
  display: block;
  margin-bottom: 5rpx;
  font-size: 28rpx;
}

.best-formula {
  margin-bottom: 30rpx;
  padding: 20rpx;
  background-color: #e8f8f0;
  border-radius: 10rpx;
  border: 1px solid #27ae60;
}

.best-title {
  font-weight: bold;
  margin-bottom: 10rpx;
  display: block;
}

.calculator {
  margin-bottom: 30rpx;
  padding: 20rpx;
  background-color: #f0f4f8;
  border-radius: 10rpx;
  border: 1px solid #ddd;
}

.calc-title {
  font-weight: bold;
  margin-bottom: 15rpx;
  display: block;
}

.calc-inputs {
  display: flex;
  margin-bottom: 15rpx;
}

.calc-inputs input {
  flex: 1;
  margin-right: 10rpx;
  height: 70rpx;
  border: 1px solid #ddd;
  border-radius: 6rpx;
  padding: 0 10rpx;
  background-color: #fff;
}

.chart-container {
  margin-top: 30rpx;
  background-color: #f9f9f9;
  border: 1px solid #eee;
  border-radius: 10rpx;
  padding: 20rpx;
}

.chart-title {
  font-weight: bold;
  margin-bottom: 20rpx;
  display: block;
  text-align: center;
}

.chart-area {
  display: flex;
  justify-content: center;
  align-items: center;
}

.chart-canvas {
  width: 100%;
  height: 300px;
  max-width: 900px;
  margin: 0 auto;
}

.r-diff {
  font-size: 24rpx;
  color: #666;
  margin-top: 5rpx;
}

@media (min-width: 700px) {
  .chart-container,
  .chart-area {
    width: 750px;
    max-width: 100%;
    margin: 0 auto;
    padding: 0;
    box-sizing: border-box;
  }
  .chart-canvas {
    width: 700px !important;
    height: 400px !important;
    display: block;
    margin: 0 auto;
  }
}
</style>
